/**
 * @author suxiaoxin
 */

const aUniqueVerticalStringNotFoundInData = "___UNIQUE_VERTICAL___";
const aUniqueCommaStringNotFoundInData = "___UNIQUE_COMMA___";
const segmentSeparateChar = "|";
const methodAndArgsSeparateChar = ":";
const argsSeparateChar = ",";

const md5 = require("md5");
const sha = require("sha.js");
const Base64 = require("js-base64").Base64;

const stringHandles = {
	md5: function (str) {
		return md5(str);
	},

	sha: function (str, arg) {
		return sha(arg).update(str).digest("hex");
	},

	/**
	 * type: sha1 sha224 sha256 sha384 sha512
	 */
	sha1: function (str) {
		return sha("sha1").update(str).digest("hex");
	},

	sha224: function (str) {
		return sha("sha224").update(str).digest("hex");
	},

	sha256: function (str) {
		return sha("sha256").update(str).digest("hex");
	},

	sha384: function (str) {
		return sha("sha384").update(str).digest("hex");
	},

	sha512: function (str) {
		return sha("sha512").update(str).digest("hex");
	},

	base64: function (str) {
		return Base64.encode(str);
	},

	unbase64: function (str) {
		return Base64.decode(str);
	},

	substr: function (str, ...args) {
		return str.substr(...args);
	},

	concat: function (str, ...args) {
		args.forEach(item => {
			str += item;
		});
		return str;
	},

	lconcat: function (str, ...args) {
		args.forEach(item => {
			str = item + this._string;
		});
		return str;
	},

	lower: function (str) {
		return str.toLowerCase();
	},

	upper: function (str) {
		return str.toUpperCase();
	},

	length: function (str) {
		return str.length;
	},

	number: function (str) {
		return !isNaN(str) ? +str : str;
	}
};

let handleValue = function (str) {
	return str;
};

const _handleValue = function (str) {
	if (str[0] === str[str.length - 1] && (str[0] === '"' || str[0] === "'")) {
		str = str.substr(1, str.length - 2);
	}
	return handleValue(
		str
			.replace(
				new RegExp(aUniqueVerticalStringNotFoundInData, "g"),
				segmentSeparateChar
			)
			.replace(
				new RegExp(aUniqueCommaStringNotFoundInData, "g"),
				argsSeparateChar
			)
	);
};

class PowerString {
	constructor(str) {
		this._string = str;
	}

	toString() {
		return this._string;
	}
}

function addMethod(method, fn) {
	PowerString.prototype[method] = function (...args) {
		args.unshift(this._string + "");
		this._string = fn.apply(this, args);
		return this;
	};
}

function importMethods(handles) {
	for (let method in handles) {
		addMethod(method, handles[method]);
	}
}

importMethods(stringHandles);

function handleOriginStr(str, handleValueFn) {
	if (!str) return str;
	if (typeof handleValueFn === "function") {
		handleValue = handleValueFn;
	}
	str = str
		.replace("\\" + segmentSeparateChar, aUniqueVerticalStringNotFoundInData)
		.replace("\\" + argsSeparateChar, aUniqueCommaStringNotFoundInData)
		.split(segmentSeparateChar)
		.map(handleSegment)
		.reduce(execute, null)
		.toString();
	return str;
}

function execute(str, curItem, index) {
	if (index === 0) {
		return new PowerString(curItem);
	}
	return str[curItem.method].apply(str, curItem.args);
}

function handleSegment(str, index) {
	str = str.trim();
	if (index === 0) {
		return _handleValue(str);
	}

	let method,
		args = [];
	if (str.indexOf(methodAndArgsSeparateChar) > 0) {
		str = str.split(methodAndArgsSeparateChar);
		method = str[0].trim();
		args = str[1]
			.split(argsSeparateChar)
			.map(item => _handleValue(item.trim()));
	} else {
		method = str;
	}
	if (typeof stringHandles[method] !== "function") {
		throw new Error(`This method name(${method}) is not exist.`);
	}

	return {
		method,
		args
	};
}

module.exports = {
	utils: stringHandles,
	PowerString,
	/**
	 * 类似于 angularJs的 filter 功能
	 * @params string
	 * @params fn 处理参数值函数，默认是一个返回原有参数值函数
	 *
	 * @expamle
	 * filter('string | substr: 1, 10 | md5 | concat: hello ')
	 */
	filter: handleOriginStr
};
